package com.zeno.chinesecalendar;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author liqiang
 * @date 2019/5/27 11:54
 * @description 计算农历节假日信息
 *         SimpleCalendar.Element element=SimpleCalendar.getCalendarDetail("2019-06-06","yyyy-MM-dd");
 *         element=SimpleCalendar.getCalendarDetail("2019-06-04","yyyy-MM-dd");
 */
public class SimpleCalendar {
    private static final Pattern p = Pattern.compile("^(\\d{2})(\\d{2})([\\s\\*])(.+)$");

    private static final  long[] LUNAR_INFO = new long[]{
            0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af, 0x9ad0, 0x55d2,
            0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd255, 0xb54f, 0xd6a0, 0xada2, 0x95b0, 0x4977,
            0x497f, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970,
            0x6566, 0xd4a0, 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f,
            0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2, 0xa950, 0xb557,
            0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573, 0x52bf, 0xa9a8, 0xe950, 0x6aa0,
            0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0,
            0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6,
            0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46, 0xab60, 0x9570,
            0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58, 0x5ac0, 0xab60, 0x96d5, 0x92e0,
            0xc960, 0xd954, 0xd4a0, 0xda50, 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5,
            0xa950, 0xb4a0, 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930,
            0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530,
            0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0, 0xd0b6, 0xd25f, 0xd520, 0xdd45,
            0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0,
            0x4b63, 0x937f, 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef,
            0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0, 0xa6d0, 0x55d4,
            0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50, 0x55a0, 0xaba4, 0xa5b0, 0x52b0,
            0xb273, 0x6930, 0x7337, 0x6aa0, 0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260,
            0xe968, 0xd520, 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252,
            0xd520};

    List<Element> elements=new ArrayList<Element>();
    public static  Map<String,SimpleCalendar> cache=new HashMap<String,SimpleCalendar>();
    private static final long[] solarMonth = new long[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    private static final String[] CELESTIAL = new String[]{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
    public static final String[] TERRESTRIAL = new String[]{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};

    private static final String[] FIRST_DAY_TIME = new String[]{"甲子","乙丑","丙寅","丁卯","戊辰","己巳","庚午","辛未","壬申", "癸酉","甲戌","乙亥"};
    private static final String[] SECOND_DAY_TIME = new String[]{ "丙子","丁丑","戊寅","己卯","庚辰","辛巳","壬午","癸未","甲申", "乙酉","丙戌","丁亥"};
    private static final String[] THIRD_DAY_TIME = new String[]{ "戊子","己丑","庚寅","辛卯","壬辰","癸巳","甲午","乙未","丙申", "丁酉","戊戌","己亥"};
    private static final String[] FORTH_DAY_TIME = new String[]{ "庚子","辛丑","壬寅","癸卯","甲辰","乙巳","丙午","丁未","戊申", "己酉","庚戌","辛亥"};
    private static final String[] FIFTH_DAY_TIME = new String[]{ "壬子","癸丑","甲寅","乙卯","丙辰","丁巳","戊午","己未","庚申", "辛酉","壬戌","癸亥"};

    private static final String[] CHINESE_ZODIAC = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};
    private static final String[] solarTerm = new String[]{"小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"};
    private static final int[] sTermInfo = new int[]{0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758};
    private static final char[] nStr1 = new char[]{'日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'};
    private static final String[] nStr2 = new String[]{"初", "十", "廿", "卅", " "};

    private static final  String[] monthChinese=new String[]{ "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二" };
    private static final   String []dayChinese=new String[]{"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二","十三","十四","十五","十六","十七","十八","十九","二十","二十一","二十二","二十三","二十四","二十五","二十六","二十七","二十八","二十九","三十","三十一"};
    private static final char[] jcName0 = new char[]{'建', '除', '满', '平', '定', '执', '破', '危', '成', '收', '开', '闭'};
    private static final char[] jcName1 = new char[]{'闭', '建', '除', '满', '平', '定', '执', '破', '危', '成', '收', '开'};
    private static final char[] jcName2 = new char[]{'开', '闭', '建', '除', '满', '平', '定', '执', '破', '危', '成', '收'};
    private static final char[] jcName3 = new char[]{'收', '开', '闭', '建', '除', '满', '平', '定', '执', '破', '危', '成'};
    private static final char[] jcName4 = new char[]{'成', '收', '开', '闭', '建', '除', '满', '平', '定', '执', '破', '危'};
    private static final char[] jcName5 = new char[]{'危', '成', '收', '开', '闭', '建', '除', '满', '平', '定', '执', '破'};
    private static final char[] jcName6 = new char[]{'破', '危', '成', '收', '开', '闭', '建', '除', '满', '平', '定', '执'};
    private static final char[] jcName7 = new char[]{'执', '破', '危', '成', '收', '开', '闭', '建', '除', '满', '平', '定'};
    private static final char[] jcName8 = new char[]{'定', '执', '破', '危', '成', '收', '开', '闭', '建', '除', '满', '平'};
    private static final char[] jcName9 = new char[]{'平', '定', '执', '破', '危', '成', '收', '开', '闭', '建', '除', '满'};
    private static final char[] jcName10 = new char[]{'满', '平', '定', '执', '破', '危', '成', '收', '开', '闭', '建', '除'};
    private static final char[] jcName11 = new char[]{'除', '满', '平', '定', '执', '破', '危', '成', '收', '开', '闭', '建'};


    /** 公历当月天数 */
    private Long length;
    /** 公历当月1日星期几 */
    private int firstWeek;

    public static Element getCalendarDetail(Date date) throws ParseException {

        Calendar cal = Calendar.getInstance() ;
        cal.setTime(date);

        return getMonthCalendar(cal).get(cal.get(Calendar.DATE)-1);
    }

    public static List<Element> getMonthCalendar(Calendar cal) throws ParseException {

        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH);
        String cacheKey=(year+"-"+month);
        SimpleCalendar lunarCalendarUtil = cache.get(cacheKey);
        if(lunarCalendarUtil == null){
            lunarCalendarUtil=new SimpleCalendar();
            lunarCalendarUtil.calendar(year, month);
            cache.put(cacheKey,lunarCalendarUtil);
        }

        return lunarCalendarUtil.getElements();
    }

    public static Element getCalendarDetail(String date,String pattern) throws ParseException {
        SimpleDateFormat df2 = new SimpleDateFormat(pattern);
        return getCalendarDetail(df2.parse(date));
    }

    public List<Element> getElements() {
        return elements;
    }

    public void calendar(int y, int m) throws ParseException {
        Date sDObj = null;
        Lunar lDObj = null;
        Boolean lL=null;
        Long lD2=null;
        Integer lY = null, lM=null, lD = 1, lX = 0, tmp1, tmp2, lM2, lY2=null, tmp3, dayglus, bsg, xs, xs1, fs, fs1, cs, cs1=null;
        // 年柱,月柱,日柱
        String cY, cM, cD;
        Integer[] lDPOS = new Integer[3];
        Integer n = 0;
        Integer firstLM = 0;
        String dateString = y + "-" +(m+1) + "-" + 1;
        sDObj = new SimpleDateFormat("yyyy-MM-dd").parse(dateString);

        //公历当月天数
        this.length = solarDays(y, m);
        //公历当月1日星期几
        this.firstWeek = sDObj.getDay();

        ////////年柱 1900年立春后为庚子年(60进制36)
        if (m < 2){
            cY = cyclical(y - 1900 + 36 - 1);
        }
        else {
            cY = cyclical(y - 1900 + 36);
        }
        //立春日期
        int term2 = sTerm(y, 2);

        ////////月柱 1900年1月小寒以前为 丙子月(60进制12)
        //返回当月「节」为几日开始
        int firstNode = sTerm(y, m * 2);
        cM = cyclical((y - 1900) * 12 + m + 12);

        lM2 = (y - 1900) * 12 + m + 12;
        //当月一日与 1900/1/1 相差天数
        //1900/1/1与 1970/1/1 相差25567日, 1900/1/1 日柱为甲戌日(60进制10)
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        df2.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date date = df2.parse("" + y + "-" + (m+1) + "-" + 1 + " 00:00:00");

        long dayCyclical = date.getTime() / 86400000 + 25567 + 10;
        //// long dayCyclical =date.getTime() / 86400000 + 25567 + 10;
        SimpleDateFormat df3 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        for (int i = 0; i < this.length; i++) {
            if (lD > lX) {
                //当月一日日期
                sDObj= df3.parse("" + y + "-" + (m+1) + "-" + (i+1) + " 00:00:00");
                //农历
                lDObj = new Lunar(sDObj);
                //农历年
                lY = lDObj.year;
                //农历月
                lM = lDObj.month;
                //农历日
                lD = lDObj.day;
                //农历是否闰月
                lL = lDObj.isLeap;
                //农历当月最后一天
                lX = lL ? leapDays(lY) : monthDays(lY, lM);

                if (n == 0){
                    firstLM = lM;
                }

                lDPOS[n++] = i - lD + 1;
            }

            //依节气调整二月分的年柱, 以立春为界
            if (m == 1 && (i + 1) == term2) {
                cY = cyclical(y - 1900 + 36);
                lY2 = (y - 1900 + 36);
            }
            //依节气月柱, 以「节」为界
            if ((i + 1) == firstNode) {
                cM = cyclical((y - 1900) * 12 + m + 13);
                lM2 = (y - 1900) * 12 + m + 13;
            }
            //日柱
            cD = cyclical(dayCyclical + i);
            lD2 = (dayCyclical + i);
            Element element = new Element(y, m + 1, i + 1, (nStr1[(i + this.firstWeek) % 7]),
                    lY, lM, lD++, lL,
                    cY, cM, cD);
            element.setcDay2(cDay(element.getlDay()));
            int paramterLy2=lY2==null?-1:(lY2 % 12);
            int paramterLm2=lM2==null?-1:lM2 % 12;
            long paramterLd2=lD2==null?-1:lD2 % 12;
            int paramterLy2b=lY2==null?-1:lY2 % 10;
            int paramterLy2c= (int) (lD2==null?-1:lD2 % 10);
            int paramterLld=lD==null?-1:lD - 1;
            element.setSgz5(CalConv2(paramterLy2, paramterLm2, (int) paramterLd2, paramterLy2b,paramterLy2c , lM,paramterLld , m + 1, cs1==null?-1:cs1));
            element.setSgz3(cyclical6(lM2 % 12, (int) ((lD2) % 12)));
            elements.add(element);
        }

        //节气
        tmp1 = sTerm(y, m * 2) - 1;
        tmp2 = sTerm(y, m * 2 + 1) - 1;
        elements.get(tmp1).solarTerms = solarTerm[m * 2];
        elements.get(tmp2).solarTerms = solarTerm[m * 2 + 1];
        if (m == 3){
            elements.get(tmp1).color = "red"; //清明颜色
        }


        //国历节日
        for (String i : CalendarConstant.sFtv){
            Matcher matcher = p.matcher(i);
            if (matcher.matches()) {
                if(i.equals("1212  西安事变纪念日")){
                    int j=2;
                }
                if (Integer.valueOf(matcher.group(1)).intValue() == (m + 1)) {
                    elements.get(Integer.valueOf(matcher.group(2)) - 1).solarFestival +=matcher.group(4)+"";
                    if (matcher.group(3).equals('*'))   elements.get(Integer.valueOf(matcher.group(0)) - 1).color = "red";
                }
            }
        }

        //农历节日
        for (String i  :  CalendarConstant.lFtv){
            Matcher matcher=p.matcher(i);
            if (matcher.matches()) {
                tmp1 = Integer.valueOf(matcher.group(1)) - firstLM;
                if (tmp1 == -11) tmp1 = 1;
                if (tmp1 >= 0 && tmp1 < n) {
                    tmp2 = lDPOS[tmp1] +Integer.valueOf(matcher.group(2)) - 1;
                    if (tmp2 >= 0 && tmp2 < this.length) {
                        elements.get(tmp2).lunarFestival += matcher.group(4);
                        if (matcher.group(3).equals("*")) elements.get(tmp2).color = "red";
                    }
                }
            }
        }

        //复活节只出现在3或4月
        if (m == 2 || m == 3) {
            Easter estDay = new Easter(y);
            if (m == estDay.m) {
                elements.get(estDay.d - 1).solarFestival = elements.get(estDay.d - 1).solarFestival + " 复活节(Easter Sunday)";
            }
        }


        //黑色星期五
        if ((this.firstWeek + 12) % 7 == 5) {
            elements.get(12).solarFestival += "黑色星期五";
        }

        //今日
        //if (y == tY && m == tM) this[tD - 1].isToday = true;
    }
    //==============================返回公历 y年某m+1月的天数
    public long solarDays(int y, int m) {
        if (m == 1) {
            return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28);
        }
        else {
            return (solarMonth[m]);
        }
    }

    /** 返回阴历 (y年,m+1月) */
    public char cyclical6(int num, int num2) {
        switch (num){
            case 0: return(jcName0[num2]);
            case 1: return(jcName1[num2]);
            case 2: return(jcName2[num2]);
            case 3: return(jcName3[num2]);
            case 4: return(jcName4[num2]);
            case 5: return(jcName5[num2]);
            case 6: return(jcName6[num2]);
            case 7: return(jcName7[num2]);
            case 8: return(jcName8[num2]);
            case 9: return(jcName9[num2]);
            case 10: return(jcName10[num2]);
            case 11: return(jcName11[num2]);
            default:
        }
        return '0';
    }
    public String  CalConv2(int yy,int  mm,int dd,int y,int d,int m, int dt,int  nm,int nd) {
        int dy = d  + dd;
        if ((yy == 0 && dd == 6) || (yy == 6 && dd == 0) || (yy == 1 && dd == 7) || (yy == 7 && dd == 1) || (yy == 2 && dd == 8) || (yy == 8 && dd == 2) || (yy == 3 && dd == 9) || (yy == 9 && dd == 3) || (yy == 4 && dd == 10) || (yy == 10 && dd == 4) || (yy == 5 && dd == 11) || (yy == 11 && dd == 5)) {
            return "<FONT color=#0000A0>日值岁破 大事不宜</font>";
        }
        else if ((mm == 0 && dd == 6) || (mm == 6 && dd == 0) || (mm == 1 && dd == 7) || (mm == 7 && dd == 1) || (mm == 2 && dd == 8) || (mm == 8 && dd == 2) || (mm == 3 && dd == 9) || (mm == 9 && dd == 3) || (mm == 4 && dd == 10) || (mm == 10 && dd == 4) || (mm == 5 && dd == 11) || (mm == 11 && dd == 5)) {
            return "<FONT color=#0000A0>日值月破 大事不宜</font>";
        }
        else if ((y == 0 && dy == 911) || (y == 1 && dy == 55) || (y == 2 && dy == 111) || (y == 3 && dy == 75) || (y == 4 && dy == 311) || (y == 5 && dy == 9) || (y == 6 && dy == 511) || (y == 7 && dy == 15) || (y == 8 && dy == 711) || (y == 9 && dy == 35)) {
            return "<FONT color=#0000A0>日值上朔 大事不宜</font>";
        }
        else if ((m == 1 && dt == 13) || (m == 2 && dt == 11) || (m == 3 && dt == 9) || (m == 4 && dt == 7) || (m == 5 && dt == 5) || (m == 6 && dt == 3) || (m == 7 && dt == 1) || (m == 7 && dt == 29) || (m == 8 && dt == 27) || (m == 9 && dt == 25) || (m == 10 && dt == 23) || (m == 11 && dt == 21) || (m == 12 && dt == 19)) {
            return "<FONT color=#0000A0>日值杨公十三忌 大事不宜</font>";
        }
        else {
            return "0";
        }
    }

    //============================== 传入 offsenew Datet 返回干支, 0=甲子
    public String cyclical(long num) {
        return(CELESTIAL[(int) (num % 10)] + TERRESTRIAL[(int) (num % 12)]);
    }
    //======================  中文日期
    public String cDay(int d) {
        String s;

        switch (d) {
            case 10: return  "初十";
            case 20: return  "二十";
            case 30: return "三十";
            default:
        }

        s = nStr2[Double.valueOf(Math.floor(d / 10)).intValue()];
        s += nStr1[d % 10];
        return s;
    }
    /**
     * 某年的第n个节气为几日(从0小寒起算)
     * @param y
     * @param n
     * @return
     * @throws ParseException
     */
    public int sTerm(int  y,int  n) throws ParseException {
        SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        df2.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date date = df2.parse("1900-01-06 02:05:00");
        Long utcTime2=date.getTime();
        BigDecimal time2 = new BigDecimal(31556925974.7).multiply(new BigDecimal(y - 1900))
                .add(new BigDecimal(sTermInfo[n]).multiply(BigDecimal.valueOf(60000L)));
        BigDecimal time = time2.add(BigDecimal.valueOf(utcTime2));
        Date offDate = new Date(time.longValue());
        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(TimeZone.getTimeZone("UTC"));
        cal.setTime(offDate);
        int utcDate=cal.get(Calendar.DATE);

        // 日期从0算起
        return utcDate;
    }

    /**
     * 返回农历 y年闰哪个月 1-12 , 没闰返回 0
     * @param y
     * @return
     */
    public Long  leapMonth(int y) {
        long lm = LUNAR_INFO[y - 1900] & 0xf;
        return(lm == 0xf ? 0 : lm);
    }

    /**
     * 返回农历 y年的总天数
     * @param y
     * @return
     */
    public Long lYearDays(int y) {
        long i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1){
            sum += (LUNAR_INFO[y - 1900] & i)!=0 ? 1 : 0;
        }
        return(sum + leapDays(y));
    }

    //====================================== 返回农历 y年闰月的天数
    public int leapDays(int y) {
        if (leapMonth(y)!=0) {
            return ((LUNAR_INFO[y - 1899] & 0xf) == 0xf ? 30 : 29);
        }

        return 0;
    }
    //====================================== 返回农历 y年m月的总天数
    private int monthDays(int y,int m) {
        return( (LUNAR_INFO[y - 1900] & (0x10000 >> m))!=0 ? 30 : 29 );
    }

    public class Lunar{
        private  int year;
        private boolean isLeap;
        private  int month;
        private  int day;
        public Lunar(Date objDate) throws ParseException {
            int i, leap = 0, temp = 0;
            SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            df2.setTimeZone(TimeZone.getTimeZone("UTC"));
            DateFormat dtFmt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Date date = df2.parse(dtFmt.format(objDate));
            Date date3 = df2.parse("" + 1900 + "-" + 1 + "-" + 31 + " 00:00:00");
            long time1=date.getTime();
            long time2=date3.getTime();
            int offset = (int)(( time1-time2) / 86400000);
            for (i = 1900; i < 2100 && offset > 0; i++) {
                temp = lYearDays(i).intValue();
                offset -= temp;
            }

            if (offset < 0) {
                offset += temp;
                i--;
            }

            this.year = i;
            //闰哪个月
            leap = leapMonth(i).intValue();
            this.isLeap = false;

            for (i = 1; i < 13 && offset > 0; i++) {
                //闰月
                if (leap > 0 && i == (leap + 1) && !this.isLeap) {
                    --i;
                    this.isLeap = true;
                    temp = leapDays(this.year);
                }
                else {
                    temp = monthDays(this.year, i);
                }

                //解除闰月
                if (this.isLeap && i == (leap + 1)) {
                    this.isLeap = false;
                }

                offset -= temp;
            }

            if (offset == 0 && leap > 0 && i == leap + 1) {
                this.isLeap = !this.isLeap;
                if (this.isLeap) {
                    --i;
                }
            }

            if (offset < 0) {
                offset += temp;
                --i;
            }

            this.month = i;
            this.day = offset + 1;
        }
    }

    public class Easter{

        public int m;
        public int d;
        public Easter(int y) throws ParseException {
            int term2 = sTerm(y, 5); //取得春分日期
            SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            df2.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date dayTerm2 = df2.parse("" +y + "-" +3 + "-" + term2 + " 00:00:00");//取得春分的公历日期控件(春分一定出现在3月)
            Lunar lDayTerm2 = new Lunar(dayTerm2); //取得取得春分农历
            int lMlen=0;
            //取得下个月圆的相差天数
            if (lDayTerm2.day < 15) {
                lMlen = 15 - lDayTerm2.day;
            }
            else {
                lMlen = (lDayTerm2.isLeap ? leapDays(y) : monthDays(y, lDayTerm2.month)) - lDayTerm2.day + 15;
            }

            //一天等于 1000*60*60*24 = 86400000 毫秒
            Date l15 = new Date(dayTerm2.getTime() + 86400000 * lMlen); //求出第一次月圆为公历几日
            Date dayEaster = new Date(l15.getTime() + 86400000 * ( 7 - l15.getDay() )); //求出下个周日

            this.m = dayEaster.getMonth();
            this.d = dayEaster.getDate();
        }
    }

    public static String getSemBranchHour(char branchDay,short hour){

        int index = 0;
        switch (hour){
            case 21: case 22:
                ++index;
            case 19: case 20:
                ++index;
            case 17: case 18:
                ++index;
            case 15: case 16:
                ++index;
            case 13: case 14:
                ++index;
            case 11: case 12:
                ++index;
            case 9: case 10:
                ++index;
            case 7: case 8:
                ++index;
            case 5: case 6:
                ++index;
            case 3: case 4:
                ++index;
            case 1: case 2:
                ++index;
            case 23: case 0:
            default:
        }

        switch (branchDay){
            case '甲': case '己':
                return FIRST_DAY_TIME[index];
            case '乙': case '庚':
                return SECOND_DAY_TIME[index];
            case '丙': case '辛':
                return THIRD_DAY_TIME[index];
            case '丁': case '壬':
                return FORTH_DAY_TIME[index];
            case '戊': case '癸':
                return FIFTH_DAY_TIME[index];
            default:
                break;
        }

        return null;
    }

    public static  class Element{
        /** 公元年4位数字 */
        public  int sYear;
        /** 公元月数字 */
        public int  sMonth;
        /** 公元日数字 */
        public int sDay;
        /** 星期, 1个中文 */
        public char  week;
        public int lYear;
        public int  lMonth;
        public String lMonthChinese;
        public String lDayChinese;
        public int  lDay;
        public boolean  isLeap;
        public String  cYear;
        public String cMonth;
        public String cDay;
        public String cDay2;
        public String cHour;
        public String color;
        public boolean isToday;
        public String lunarFestival;
        public String solarFestival;
        public String solarTerms;
        public String sgz5;
        public char sgz3;

        public Element(int sYear,int  sMonth, int sDay,char  week,int lYear,int  lMonth,int  lDay,boolean  isLeap,String  cYear, String cMonth, String cDay) {

            this.isToday = false;
            //瓣句
            this.sYear = sYear;
            this.sMonth = sMonth;
            this.sDay = sDay;
            this.week = week;

            //农历
            this.lYear = lYear;   //公元年4位数字
            this.lMonth = lMonth;  //农历月数字
            this.lDay = lDay;    //农历日数字
            this.isLeap = isLeap;  //是否为农历闰月?
            //中文
            this.lMonthChinese = monthChinese[lMonth-1];
            this.lDayChinese = dayChinese[lDay-1];
            //八字
            this.cYear = cYear;   //年柱, 2个中文
            this.cMonth = cMonth;  //月柱, 2个中文
            this.cDay = cDay;    //日柱, 2个中文

            this.color = "";

            this.lunarFestival = ""; //农历节日
            this.solarFestival = ""; //公历节日
            this.solarTerms = ""; //节气
        }

        public String getSgz5() {
            return sgz5;
        }

        public void setSgz5(String sgz5) {
            this.sgz5 = sgz5;
        }

        public char getSgz3() {
            return sgz3;
        }

        public void setSgz3(char sgz3) {
            this.sgz3 = sgz3;
        }

        public int getsYear() {
            return sYear;
        }

        public void setsYear(int sYear) {
            this.sYear = sYear;
        }

        public int getsMonth() {
            return sMonth;
        }

        public void setsMonth(int sMonth) {
            this.sMonth = sMonth;
        }

        public int getsDay() {
            return sDay;
        }

        public void setsDay(int sDay) {
            this.sDay = sDay;
        }

        public String getcDay2() {
            return cDay2;
        }

        public void setcDay2(String cDay2) {
            this.cDay2 = cDay2;
        }

        public String getcHour() {
            return cHour;
        }
        public void setcHour(String cHour) {
            this.cHour = cHour;
        }

        public char getWeek() {
            return week;
        }

        public void setWeek(char week) {
            this.week = week;
        }

        public int getlYear() {
            return lYear;
        }

        public void setlYear(int lYear) {
            this.lYear = lYear;
        }

        public int getlMonth() {
            return lMonth;
        }

        public void setlMonth(int lMonth) {
            this.lMonth = lMonth;
        }

        public int getlDay() {
            return lDay;
        }

        public void setlDay(int lDay) {
            this.lDay = lDay;
        }

        public boolean isLeap() {
            return isLeap;
        }

        public void setLeap(boolean leap) {
            isLeap = leap;
        }

        public String getcYear() {
            return cYear;
        }

        public void setcYear(String cYear) {
            this.cYear = cYear;
        }

        public String getcMonth() {
            return cMonth;
        }

        public void setcMonth(String cMonth) {
            this.cMonth = cMonth;
        }

        public String getcDay() {
            return cDay;
        }

        public void setcDay(String cDay) {
            this.cDay = cDay;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public boolean isToday() {
            return isToday;
        }

        public void setToday(boolean today) {
            isToday = today;
        }

        public String getLunarFestival() {
            return lunarFestival;
        }

        public void setLunarFestival(String lunarFestival) {
            this.lunarFestival = lunarFestival;
        }

        public String getSolarFestival() {
            return solarFestival;
        }

        public void setSolarFestival(String solarFestival) {
            this.solarFestival = solarFestival;
        }

        public String getSolarTerms() {
            return solarTerms;
        }

        public void setSolarTerms(String solarTerms) {
            this.solarTerms = solarTerms;
        }

        public String getlMonthChinese() {
            return lMonthChinese;
        }

        public void setlMonthChinese(String lMonthChinese) {
            this.lMonthChinese = lMonthChinese;
        }

        public String getlDayChinese() {
            return lDayChinese;
        }

        public void setlDayChinese(String lDayChinese) {
            this.lDayChinese = lDayChinese;
        }
    }

    public static void main(String[] args) throws ParseException {

        Element e = getCalendarDetail("2021-11-8","yyyy-MM-dd");


        for (short i=0; i<24; i += 2){
            System.out.print(getSemBranchHour(e.getcDay().charAt(0),i) + ' ');
        }
    }

}
